home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / DefaultListSelectionModel.java < prev    next >
Text File  |  1998-06-30  |  18KB  |  671 lines

  1. /*
  2.  * @(#)DefaultListSelectionModel.java    1.35 98/02/02
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20.  
  21. package com.sun.java.swing;
  22.  
  23. import java.util.EventListener;
  24. import java.util.BitSet;
  25. import java.io.Serializable;
  26.  
  27. import com.sun.java.swing.event.*;
  28.  
  29.  
  30. /**
  31.  * Default data model for list selections.
  32.  * <p>
  33.  * Warning: serialized objects of this class will not be compatible with
  34.  * future swing releases.  The current serialization support is appropriate 
  35.  * for short term storage or RMI between Swing1.0 applications.  It will
  36.  * not be possible to load serialized Swing1.0 objects with future releases
  37.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  38.  * baseline for the serialized form of Swing objects.
  39.  *
  40.  * @version 1.35 02/02/98
  41.  * @author Hans Muller
  42.  * @author Philip Milne
  43.  * @see ListSelectionModel
  44.  */
  45.  
  46. public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, Serializable
  47. {
  48.     private int selectionMode = MULTIPLE_INTERVAL_SELECTION;
  49.     private int minIndex = -1;
  50.     private int maxIndex = -1;
  51.     private int anchorIndex = -1;
  52.     private int leadIndex = -1;
  53.     private int firstChangedIndex = -1;
  54.     private int lastChangedIndex = -1;
  55.     private boolean isAdjusting = false;
  56.  
  57.     protected BitSet value = new BitSet(32);
  58.     protected EventListenerList listenerList = new EventListenerList();
  59.  
  60.     protected boolean leadAnchorNotificationEnabled = true;
  61.     
  62.     public int getMinSelectionIndex() { return minIndex; }
  63.     public int getMaxSelectionIndex() { return maxIndex; }
  64.  
  65.     public boolean getValueIsAdjusting() { return isAdjusting; }
  66.  
  67.  
  68.     public int getSelectionMode() { return selectionMode; }
  69.  
  70.     public void setSelectionMode(int selectionMode) {
  71.     switch (selectionMode) {
  72.     case SINGLE_SELECTION:
  73.     case SINGLE_INTERVAL_SELECTION:
  74.     case MULTIPLE_INTERVAL_SELECTION:
  75.         this.selectionMode = selectionMode;
  76.         break;
  77.     default:
  78.         throw new IllegalArgumentException("invalid selectionMode");
  79.     }
  80.     }
  81.  
  82.     public boolean isSelectedIndex(int index) {
  83.     return ((index == -1) || (index < minIndex) || (index > maxIndex)) ? false : value.get(index);
  84.     }
  85.  
  86.     public boolean isSelectionEmpty() {
  87.     return (minIndex == -1) && (maxIndex == -1);
  88.     }
  89.  
  90.     public void addListSelectionListener(ListSelectionListener l) {
  91.      listenerList.add(ListSelectionListener.class, l);
  92.     }
  93.  
  94.     public void removeListSelectionListener(ListSelectionListener l) {
  95.      listenerList.remove(ListSelectionListener.class, l);
  96.     }
  97.  
  98.  
  99.     /**
  100.      * Notify listeners that we are beginning or ending a
  101.      * series of value changes
  102.      */
  103.     protected void fireValueChanged(boolean isAdjusting) {
  104.     fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(), isAdjusting);
  105.     }
  106.  
  107.  
  108.     /**
  109.      * Notify ListSelectionListeners that the value of the selection,
  110.      * in the closed interval firstIndex,lastIndex, has changed.
  111.      */
  112.     protected void fireValueChanged(int firstIndex, int lastIndex) {
  113.     fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
  114.     }
  115.  
  116.  
  117.     /**
  118.      * @param firstIndex The first index in the interval.
  119.      * @param index1 The last index in the interval.
  120.      * @param isAdjusting True if this is the final change in a series of them.
  121.      * @see EventListenerList
  122.      */
  123.     protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
  124.     {
  125.     Object[] listeners = listenerList.getListenerList();
  126.     ListSelectionEvent e = null;
  127.  
  128.     for (int i = listeners.length - 2; i >= 0; i -= 2) {
  129.         if (listeners[i] == ListSelectionListener.class) {
  130.         if (e == null) {
  131.             e = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
  132.         }
  133.         ((ListSelectionListener)listeners[i+1]).valueChanged(e);
  134.         }
  135.     }
  136.     }
  137.  
  138.  
  139.     public void clearSelection()
  140.     {
  141.     if ((minIndex == -1) || (maxIndex == -1)) {
  142.         return;
  143.     }
  144.  
  145.     boolean selectionChanged = false;
  146.  
  147.     for(int r = minIndex; r <= maxIndex; r++) {
  148.         if (value.get(r)) {
  149.         selectionChanged = true;
  150.         }
  151.         value.clear(r);
  152.     }
  153.  
  154.     minIndex = maxIndex = anchorIndex = leadIndex = -1;
  155.  
  156.     if (selectionChanged) {
  157.         fireValueChanged(minIndex, maxIndex);
  158.     }
  159.     }
  160.  
  161.     /**
  162.      * Sets the value of the leadAnchorNotificationEnabled flag.
  163.      * @see        #isLeadAnchorNotificationEnabled()
  164.      */
  165.     public void setLeadAnchorNotificationEnabled(boolean flag) {
  166.         leadAnchorNotificationEnabled = flag;
  167.     }
  168.  
  169.     /**
  170.      * Returns the value of the leadAnchorNotificationEnabled flag.
  171.      * When leadAnchorNotificationEnabled is true the model
  172.      * generates notification events with bounds that cover all the changes to
  173.      * the selection plus the changes to the lead and anchor indices.
  174.      * Setting the flag to false causes a norrowing of the event's bounds to
  175.      * include only the elements that have been selected or deselected since
  176.      * the last change. Either way, the model continues to maintain the lead
  177.      * and anchor variables internally. The default is true.
  178.      * @return         the value of the leadAnchorNotificationEnabled flag
  179.      * @see        #setLeadAnchorNotificationEnabled(boolean)
  180.      */
  181.     /* The JTable uses this to optimise row selection on mouse dragged.
  182.      * Setting the flag to false causes the repainting code to only redraw
  183.      * those rows that have been selected (or deselected) since the last event.
  184.      */
  185.     public boolean isLeadAnchorNotificationEnabled() {
  186.         return leadAnchorNotificationEnabled;
  187.     }
  188.  
  189.     /* If the lead or anchor indices have changed, add them to
  190.      * the firstChanged/lastChanged interval.
  191.      */
  192.     private void updateLeadAnchorIndices(int index0, int index1)
  193.     {
  194.         if (leadAnchorNotificationEnabled) {
  195.             if ((anchorIndex != -1) && (anchorIndex != index0)) {
  196.                 int minAnchorIndex = Math.min(anchorIndex, index0);
  197.                 int maxAnchorIndex = Math.max(anchorIndex, index0);
  198.  
  199.             firstChangedIndex = (firstChangedIndex == -1)
  200.                 ? maxAnchorIndex
  201.                     : Math.min(minAnchorIndex, firstChangedIndex);
  202.             lastChangedIndex = Math.max(maxAnchorIndex, lastChangedIndex);
  203.             }
  204.  
  205.             if ((leadIndex != -1) && (leadIndex != index1)) {
  206.             int minLeadIndex = Math.min(leadIndex, index1);
  207.             int maxLeadIndex = Math.max(leadIndex, index1);
  208.  
  209.             firstChangedIndex = (firstChangedIndex == -1)
  210.                 ? minLeadIndex
  211.                     : Math.min(minLeadIndex, firstChangedIndex);
  212.             lastChangedIndex = Math.max(maxLeadIndex, lastChangedIndex);
  213.             }
  214.         }
  215.  
  216.     anchorIndex = index0;
  217.     leadIndex = index1;
  218.     }
  219.  
  220.  
  221.     public void setSelectionInterval(int index0, int index1)
  222.     {
  223.     if (getSelectionMode() == SINGLE_SELECTION) {
  224.         index0 = index1;
  225.     }
  226.  
  227.     int newMinIndex, newMaxIndex;
  228.  
  229.     /* Special case, clear selection.
  230.      */
  231.  
  232.     if ((index0 == -1) && (index1 == -1)) {
  233.         clearSelection();
  234.         return;
  235.     }
  236.  
  237.     newMinIndex = Math.min(index0, index1);
  238.     newMaxIndex = Math.max(index0, index1);
  239.  
  240.     /* Union newMinIndex,newMaxIndex with minIndex,maxIndex.
  241.      */
  242.  
  243.     if ((minIndex == -1) || (newMinIndex < minIndex)) {
  244.         minIndex = newMinIndex;
  245.     }
  246.  
  247.     if ((maxIndex == -1) || (newMaxIndex > maxIndex)) {
  248.         maxIndex = newMaxIndex;
  249.     }
  250.  
  251.     /* Update the selection value bitset and keep track of the
  252.      * first and last indices that were actually changed.
  253.      */
  254.  
  255.     firstChangedIndex = -1;
  256.     lastChangedIndex = -1;
  257.  
  258.     for(int r = minIndex; r < newMinIndex; r++) {
  259.         if (value.get(r)) {
  260.         if (firstChangedIndex == -1) {
  261.             firstChangedIndex = lastChangedIndex = r;
  262.         }
  263.         else if (r > lastChangedIndex) {
  264.             lastChangedIndex = r;
  265.         }
  266.         }
  267.         value.clear(r);
  268.     }
  269.  
  270.     for(int r = newMinIndex; r <= newMaxIndex; r++) {
  271.  
  272.         if (!value.get(r)) {
  273.         if (firstChangedIndex == -1) {
  274.             firstChangedIndex = lastChangedIndex = r;
  275.         }
  276.         else if (r > lastChangedIndex) {
  277.             lastChangedIndex = r;
  278.         }
  279.         }
  280.         value.set(r);
  281.     }
  282.  
  283.     for(int r = newMaxIndex + 1; r <= maxIndex; r++) {
  284.         if (value.get(r)) {
  285.         if (firstChangedIndex == -1) {
  286.             firstChangedIndex = lastChangedIndex = r;
  287.         }
  288.         else if (r > lastChangedIndex) {
  289.             lastChangedIndex = r;
  290.         }
  291.         }
  292.         value.clear(r);
  293.     }
  294.  
  295.     updateLeadAnchorIndices(index0, index1);
  296.  
  297.     minIndex = newMinIndex;
  298.     maxIndex = newMaxIndex;
  299.  
  300.     if ((firstChangedIndex != -1) && (lastChangedIndex != -1)) {
  301.         fireValueChanged(firstChangedIndex, lastChangedIndex);
  302.     }
  303.     }
  304.  
  305.  
  306.     public void addSelectionInterval(int index0, int index1)
  307.     {
  308.     int mode = getSelectionMode();
  309.     if ((mode == SINGLE_SELECTION) || (mode == SINGLE_INTERVAL_SELECTION)) {
  310.         setSelectionInterval(index0, index1);
  311.         return;
  312.     }
  313.  
  314.     if ((index0 == -1) && (index1 == -1)) {
  315.         return;
  316.     }
  317.  
  318.     int addMinIndex = Math.min(index0, index1);
  319.     int addMaxIndex = Math.max(index0, index1);
  320.  
  321.     /* Update minIndex, maxIndex.
  322.      */
  323.  
  324.     if ((minIndex == -1) || (addMinIndex  < minIndex)) {
  325.         minIndex = addMinIndex;
  326.     }
  327.  
  328.     if ((maxIndex == -1) || (addMaxIndex > maxIndex)) {
  329.         maxIndex = addMaxIndex;
  330.     }
  331.  
  332.     /* Update the selection value bitset, and keep track of the
  333.      * first and last indices that were actually changed.
  334.      */
  335.  
  336.     firstChangedIndex = -1;
  337.     lastChangedIndex = -1;
  338.  
  339.     for(int r = addMinIndex; r <= addMaxIndex; r++) {
  340.         if (!value.get(r)) {
  341.         if (firstChangedIndex == -1) {
  342.             firstChangedIndex = lastChangedIndex = r;
  343.         }
  344.         else if (r > lastChangedIndex) {
  345.             lastChangedIndex = r;
  346.         }
  347.         value.set(r);
  348.         }
  349.     }
  350.  
  351.     updateLeadAnchorIndices(index0, index1);
  352.  
  353.     if ((firstChangedIndex != -1) && (lastChangedIndex != -1)) {
  354.         fireValueChanged(firstChangedIndex, lastChangedIndex);
  355.     }
  356.  
  357.     }
  358.  
  359.  
  360.     public void removeSelectionInterval(int index0, int index1)
  361.     {
  362.     /* No-op special case, or selection is already empty.
  363.      */
  364.  
  365.     if ((index0 == -1) && (index1 == -1)) {
  366.         return;
  367.     }
  368.  
  369.     /* Current selection is empty
  370.      */
  371.  
  372.     if ((minIndex == -1) && (maxIndex == -1)) {
  373.         return;
  374.     }
  375.  
  376.     /* Update the selection value bitset, and keep track of the
  377.      * first and last indices that were actually changed.
  378.      */
  379.  
  380.     int remMinIndex = Math.min(index0, index1);
  381.     int remMaxIndex = Math.max(index0, index1);
  382.  
  383.     int firstChangedIndex = -1;
  384.     int lastChangedIndex = -1;
  385.  
  386.     for(int r = remMinIndex; r <= remMaxIndex; r++) {
  387.         if (value.get(r)) {
  388.         if (firstChangedIndex == -1) {
  389.             firstChangedIndex = lastChangedIndex = r;
  390.         }
  391.         else if (r > lastChangedIndex) {
  392.             lastChangedIndex = r;
  393.         }
  394.         value.clear(r);
  395.         }
  396.     }
  397.  
  398.     /* Find the new min and max index values.
  399.      */
  400.  
  401.     int newMinIndex = -1;
  402.     int newMaxIndex = -1;
  403.  
  404.     for(int r = minIndex; r <= maxIndex; r++) {
  405.         if (value.get(r)) {
  406.         if (newMinIndex == -1) {
  407.             newMinIndex = newMaxIndex = r;
  408.         }
  409.         else if (r > newMaxIndex) {
  410.             newMaxIndex = r;
  411.         }
  412.         }
  413.     }
  414.  
  415.     minIndex = newMinIndex;
  416.     maxIndex = newMaxIndex;
  417.  
  418.     if ((firstChangedIndex != -1) && (lastChangedIndex != -1)) {
  419.         fireValueChanged(firstChangedIndex, lastChangedIndex);
  420.     }
  421.     }
  422.  
  423.  
  424.     /**
  425.      * Insert length indices beginning before/after index.  This is typically
  426.      * called to sync the selection model with a corresponding change
  427.      * in the data model.
  428.      */
  429.  
  430.     public void insertIndexInterval(int index, int length, boolean before)
  431.     {
  432.     /* The first new index will appear at insMinIndex and the last
  433.      * one will appear at insMaxIndex
  434.      */
  435.     int insMinIndex = (before) ? Math.max(0, index - 1) : index + 1;
  436.     int insMaxIndex = (insMinIndex + length) - 1;
  437.  
  438.     /* If both of the indices next to the insertion point were
  439.      * selected then select all of the new indices.  Special case:
  440.      * if you're inserting before index 0 then new indices will
  441.      * be selected if the index 0 is selected.
  442.      */
  443.     boolean selected = value.get(index) && value.get(insMinIndex);
  444.  
  445.     /* Right shift the entire bitset by length, beginning with
  446.      * index-1 if before is true, index+1 if it's false (i.e. with
  447.      * insMinIndex).
  448.      */
  449.     for(int i = maxIndex; i >= insMinIndex; i--) {
  450.         if (value.get(i)) {
  451.         value.set(i + length);
  452.         }
  453.         else {
  454.         value.clear(i + length);
  455.         }
  456.     }
  457.  
  458.     /* Initialize the newly inserted indices.
  459.      */
  460.     for(int i = insMinIndex; i <= insMaxIndex; i++) {
  461.         if (selected) {
  462.         value.set(i);
  463.         }
  464.         else {
  465.         value.clear(i);
  466.         }
  467.     }
  468.  
  469.     if (minIndex != -1 && (minIndex > insMinIndex || (minIndex == insMinIndex && !selected))) {
  470.         minIndex += length;
  471.     }
  472.  
  473.     if (maxIndex != -1 && maxIndex >= insMinIndex) {
  474.         maxIndex += length;
  475.     }
  476.  
  477.     fireValueChanged(insMinIndex, maxIndex);
  478.     }
  479.  
  480.  
  481.     /**
  482.      * Remove the indices in the interval index0,index1 (inclusive) from
  483.      * the selection model.  This is typically called to sync the selection
  484.      * model width a corresponding change in the data model.  Note
  485.      * that (as always) index0 need not be <= index1.
  486.      */
  487.     public void removeIndexInterval(int index0, int index1)
  488.     {
  489.     /* Special case - empty interval.
  490.      */
  491.     if ((index0 < 0) || (index1 < 0)) {
  492.         return;
  493.     }
  494.  
  495.     /* Special case - nothing's selected.
  496.      */
  497.     if ((minIndex == -1) && (maxIndex == -1)) {
  498.         return;
  499.     }
  500.  
  501.     int rmMinIndex = Math.min(index0, index1);
  502.     int rmMaxIndex = Math.max(index0, index1);
  503.     int gapLength = (rmMaxIndex - rmMinIndex) + 1;
  504.  
  505.     /* Special case - we're not removing any selected indices.
  506.      */
  507.     if ((rmMinIndex > maxIndex) || (rmMaxIndex < minIndex)) {
  508.         return;
  509.     }
  510.  
  511.     /* Special case - we're removing all of the selected indices.
  512.      */
  513.     if ((rmMinIndex <= minIndex) && (rmMaxIndex >= maxIndex)) {
  514.         clearSelection();
  515.         return;
  516.     }
  517.  
  518.     /* Shift the entire bitset to the left to close the index0, index1
  519.      * gap. Clear the bits to the right of the old maxIndex.
  520.      */
  521.     for(int i = rmMinIndex; i <= rmMaxIndex; i++) {
  522.         if (value.get(i + gapLength)) {
  523.         value.set(i);
  524.         }
  525.         else {
  526.         value.clear(i);
  527.         }
  528.     }
  529.  
  530.     for(int i = maxIndex; i > maxIndex - gapLength; i--) {
  531.         value.clear(i);
  532.     }
  533.  
  534.     /* Compute the new min and max selection indices.
  535.      */
  536.  
  537.     int oldMinIndex = minIndex;
  538.     int oldMaxIndex = maxIndex;
  539.  
  540.     if (oldMinIndex >= rmMinIndex) {
  541.         minIndex = -1;
  542.         for(int i = rmMinIndex; i <= oldMaxIndex; i++) {
  543.         if (value.get(i)) {
  544.             minIndex = i;
  545.             break;
  546.         }
  547.         }
  548.     }
  549.  
  550.     if (minIndex == -1) {
  551.         maxIndex = -1;
  552.     }
  553.     else if (oldMaxIndex > rmMaxIndex) {
  554.         maxIndex = oldMaxIndex - gapLength;
  555.     }
  556.     else {
  557.         for(int i = oldMaxIndex; i >= minIndex; i--) {
  558.         if (value.get(i)) {
  559.             maxIndex = i;
  560.             break;
  561.         }
  562.         }
  563.     }
  564.  
  565.     if ((maxIndex == -1) && (minIndex == -1)) {
  566.         fireValueChanged(rmMinIndex, rmMaxIndex);
  567.     }
  568.     else {
  569.         firstChangedIndex = Math.min(rmMinIndex, minIndex);
  570.         lastChangedIndex = Math.max(rmMaxIndex, maxIndex);
  571.         fireValueChanged(firstChangedIndex, lastChangedIndex);
  572.     }
  573.     }
  574.  
  575.  
  576.     public void setValueIsAdjusting(boolean b) {
  577.     if (b != this.isAdjusting) {
  578.         this.isAdjusting = b;
  579.         this.fireValueChanged(b);
  580.     }
  581.     }
  582.  
  583.  
  584.     public String toString() {
  585.     String s =  ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
  586.     return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
  587.     }
  588.  
  589.     /**
  590.      * Returns a clone of the reciever with the same selection.
  591.      * listenerLists are not duplicated.
  592.      *
  593.      * @exception CloneNotSupportedException if the receiver does not
  594.      *    both (a) implement the Cloneable interface and (b) define a
  595.      *    <code>clone</code> method.
  596.      */
  597.     public Object clone() throws CloneNotSupportedException {
  598.     DefaultListSelectionModel clone = (DefaultListSelectionModel)super.clone();
  599.     clone.value = (BitSet)value.clone();
  600.     clone.listenerList = new EventListenerList();
  601.     return clone;
  602.     }
  603.     
  604.     public int getAnchorSelectionIndex() { 
  605.         return anchorIndex; 
  606.     }
  607.     
  608.     public int getLeadSelectionIndex() { 
  609.         return leadIndex; 
  610.     }
  611.  
  612.     public void setAnchorSelectionIndex(int index) { 
  613.         anchorIndex = index; 
  614.     }
  615.  
  616.     public void setLeadSelectionIndex(int index) { 
  617.         updateLead(index); 
  618.         leadIndex = index;         
  619.     }
  620.     
  621. // 
  622. //  Private methods. 
  623. //
  624.  
  625.     // Return true if the index lies in the range [a, b). 
  626.     private boolean contains(int a, int b, int index) {
  627.         if (a <= b) 
  628.             return (index >= a) && (index < b); 
  629.         else 
  630.             return (index <= a) && (index > b); 
  631.     }
  632.     
  633.     private int sign(int a, int b) { 
  634.         return (a < b) ? -1 : (a > b) ? 1 : 0; 
  635.     }
  636.     
  637.     // Now that manipulation of the lead is part of the 
  638.     // selection model itself, an easier way to perform these 
  639.     // operations is to temporarily disable the event posting and 
  640.     // always do the operation in two stages - a deselection 
  641.     // followed by a selection. Do that in the next release. 
  642.     private void updateLead(int index) {
  643.         if (anchorIndex == -1) { 
  644.             anchorIndex = index;
  645.         }
  646.         
  647.         // boolean deselect = !isSelectedIndex(anchorIndex); 
  648.         boolean deselect = false; 
  649.         
  650.         // This is an edge case were the anchor is straddled by the new and old 
  651.         // lead indices. This would happen if a user were to shift click on 
  652.         // the opposite side of the anchor or move the mouse 
  653.         // very quickly from one side of the anchor to the other. 
  654.         // To update the selection we need to take two steps here, 
  655.         // one to deselect the old area and one to select the new. 
  656.         if (!deselect && contains(leadIndex, index, anchorIndex)) {
  657.             removeSelectionInterval(anchorIndex, leadIndex);
  658.             addSelectionInterval(anchorIndex, index); 
  659.         }
  660.         
  661.         if (!deselect && !contains(anchorIndex, leadIndex, index)) {
  662.             addSelectionInterval(anchorIndex, index); 
  663.         }
  664.     else {
  665.             int delta = sign(anchorIndex, leadIndex);
  666.             removeSelectionInterval(index - delta, leadIndex);
  667.         }
  668.     }
  669. }
  670.  
  671.